#pragma once
#ifndef __Oculus_KartVRFrameListener_h_
#define __Oculus_KartVRFrameListener_h_

#include "Ogre.h"
#include "OgreStringConverter.h"
#include "OgreException.h"
#include "OgreBulletCollisions.h"
#include "OgreBulletDynamics.h"
#include "Shapes/OgreBulletCollisionsStaticPlaneShape.h"	// for static planes
#include "Shapes/OgreBulletCollisionsBoxShape.h"			// for Boxes
#include "Shapes/OgreBulletCollisionsTrimeshShape.h"		// for race track
#include "Player.h"											// for Kart
#include "Oculus_RiftManager.h"								// for accessing the Oculus Rift sensor data
#include "Utils/OgreBulletCollisionsMeshToShapeConverter.h" // for race track
#include "AI_Manager.h"


//Use this define to signify OIS will be used as a DLL
//(so that dll import/export macros are in effect)
#define OIS_DYNAMIC_LIB
#include <OIS/OIS.h>

using namespace Ogre;

#ifdef USE_RTSHADER_SYSTEM
#include "OgreRTShaderSystem.h"
#endif

namespace
{
	const float g_defaultNearClip = 0.01f;
	const float g_defaultFarClip = 10000.0f;
	const float g_defaultIPD = 0.064f;
	const Ogre::ColourValue g_defaultViewportColour(97/255.0f, 97/255.0f, 200/255.0f);
	const float g_defaultProjectionCentreOffset = 0.14529906f;
	const float g_defaultDistortion[4] = {1.0f, 0.22f, 0.24f, 0};
}

class Oculus_KartVRFrameListener: public FrameListener, public WindowEventListener, public OIS::JoyStickListener
{
public:
	// Constructor takes a RenderWindow because it uses that to determine input context
	Oculus_KartVRFrameListener(
		SceneManager *sceneMgr, 
		RenderWindow *win,
		Vector3 &gravityVector,
		AxisAlignedBox &bounds,
		bool bufferedKeys = false,
		bool bufferedMouse = false,
		bool bufferedJoy = false);

#ifdef USE_RTSHADER_SYSTEM
	virtual void processShaderGeneratorInput()
	{		
		// Switch to default scheme.
		if (mKeyboard->isKeyDown(OIS::KC_F2))
		{	
			m_Cameras->getViewport()->setMaterialScheme(MaterialManager::DEFAULT_SCHEME_NAME);			
			mDebugText = "Active Viewport Scheme: ";
			mDebugText += MaterialManager::DEFAULT_SCHEME_NAME;						
		}

		// Switch to shader generator scheme.
		if (mKeyboard->isKeyDown(OIS::KC_F3))
		{
			m_Cameras->getViewport()->setMaterialScheme(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);
			mDebugText = "Active Viewport Scheme: ";
			mDebugText += RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME;
		}	

		// Toggles per pixel per light model.
		if (mKeyboard->isKeyDown(OIS::KC_F4) && mTimeUntilNextToggle <= 0)
		{	
			mTimeUntilNextToggle = 1.0;

			static bool userPerPixelLightModel = true;
			RTShader::ShaderGenerator *shaderGenerator = RTShader::ShaderGenerator::getSingletonPtr();			
			RTShader::RenderState *renderState = shaderGenerator->getRenderState(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);

			// Remove all global sub render states.
			renderState->reset();

			// Add per pixel lighting sub render state to the global scheme render state.
			// It will override the default FFP lighting sub render state.
			if (userPerPixelLightModel)
			{
				RTShader::SubRenderState *perPixelLightModel = shaderGenerator->createSubRenderState(RTShader::PerPixelLighting::Type);
				renderState->addTemplateSubRenderState(perPixelLightModel);

				mDebugText = "Per pixel lighting model applied to shader generator default scheme";
			}
			else
			{
				mDebugText = "Per vertex lighting model applied to shader generator default scheme";
			}

			// Invalidate the scheme in order to re-generate all shaders based technique related to this scheme.
			shaderGenerator->invalidateScheme(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME);

			userPerPixelLightModel = !userPerPixelLightModel;
		}	
	}

#endif

	virtual ~Oculus_KartVRFrameListener();

	//Adjust mouse clipping area
	virtual void windowResized(RenderWindow *rw);

	//Unattach OIS before window shutdown (very important under Linux)
	virtual void windowClosed(RenderWindow *rw);

	virtual bool processUnbufferedKeyInput(const FrameEvent& evt);

	virtual bool processUnbufferedMouseInput(const FrameEvent& evt);

	virtual bool axisMoved(const OIS::JoyStickEvent &arg, int axis);

	virtual bool buttonPressed(const OIS::JoyStickEvent &e, int button);

	virtual bool buttonReleased(const OIS::JoyStickEvent &e, int button);

	virtual bool povMoved(const OIS::JoyStickEvent &e, int pov);

	virtual void moveCamera();

	virtual void showDebugOverlay(bool show);

	bool frameStarted(const FrameEvent& evt);
	// Override frameRenderingQueued event to process that (don't care about frameEnded)
	bool frameRenderingQueued(const FrameEvent& evt);

	bool frameEnded(const FrameEvent& evt);

	void changeGameState(GameState state);

protected:
	//General application
	String mDebugText;
	RenderWindow *mWindow;
	bool mStatsOn;

	//Rendering
	SceneManager *mSceneMgr; 
	Oculus_RiftManager *mORM;
	Ogre::Camera *m_Cameras[2];
	Ogre::Viewport *m_viewports[2];
	Ogre::CompositorInstance *m_compositors[2];
	TextureFilterOptions mFiltering;
	int mAniso;
	int mSceneDetailIndex;
	Overlay *mDebugOverlay;

	//Physics
	OgreBulletDynamics::DynamicsWorld *mWorld;
	OgreBulletCollisions::DebugDrawer *debugDrawer;
	std::deque<OgreBulletDynamics::RigidBody *> mBodies;
	std::deque<OgreBulletCollisions::CollisionShape *> mShapes;

	//Media objects
	Ogre::Entity *mTrackRoad;
	Ogre::Entity *mTrackGrass;
	Ogre::SceneNode *mTrackRoadNode;
	Ogre::SceneNode *mTrackGrassNode;
	int mNumEntitiesInstanced;

	//Input
	OIS::InputManager *mInputManager;
	OIS::Mouse *   mMouse;
	OIS::Keyboard *mKeyboard;
	OIS::JoyStick *mJoy;
	Vector3 mTranslateVector;
	Real mCurrentSpeed;
	unsigned int mNumScreenShots;
	float mMoveScale;
	float mSpeedLimit;
	Degree mRotScale;
	Radian mRotX, mRotY;	
	Real mMoveSpeed;
	Degree mRotateSpeed;
	bool freeCam;
	char mPrevKeys[256];
	
	//Timers
	Real mTimeUntilNextToggle;
	Real mTimeSinceLastGameStateChange;
	Real mCountDown;
	Real mRaceTime;
	Real mBeepCooldown;
	Real mShowLastLapTime;

	//Sound
	OgreOggSound::OgreOggSoundManager *mSoundManager;
	SceneNode *headNode;

	//Gameplay
	GameState mGameState;
	Player *mPlayer;
	std::vector<TimingGate> mTimingGates; //First timing gate is the start/finish line.
	int mNumLaps;
	bool restartRace;

	//UI
	Gorilla::Silverback *mSilverBack;
	D3Panel *mCountDownPanel;
	Gorilla::Caption *mCountDownCaption;
	D3Panel *mSpeedPanel;
	Gorilla::Caption *mSpeedCaption;
	D3Panel *mTimerPanel;
	Gorilla::Caption *mTimerCaption;
	D3Panel *mPositionPanel;
	Gorilla::Caption *mPositionCaption;
	D3Panel *mLapPanel;
	Gorilla::Caption *mLapCaption;
	D3Panel *mRaceEndPanel;
	Gorilla::Caption *mRaceEndFinishCap;
	Gorilla::Caption *mRaceEndPosCap;
	Gorilla::Caption *mRaceEndBestLapCap;

	//AI
	AI_Manager *mAI_Manager;
	std::vector<Vector3> mAgentStartPos;
	std::vector<Waypoint> mAI_Waypoints;

	//Methods
	virtual void updateStats(void);
};
#endif // #ifndef __Oculus_KartVRFrameListener_h_